[SQL] Where句とHaving句の違い
はじめに
PostgreSQL始めました。
SQLを学ぶ過程でWhere句とHaving句の違いがわからずに悩んだ時期があったので、その時感じたことを書いておきます。
環境
MacOS 10.10.5
PostgreSQL 9.4.5
Where句でもHaving句でも同じ結果が出るけど何が違うんだろう?
結論から言うと
Where句はselect句の結果からwhere句で指定した抽出条件を実行する
Having句はGroupBy句でグルーピングした結果からHaving句で指定した抽出条件を実行する
という違いがあります。
同じ結果が出ることを確認
PostgreSQLのチュートリアルにあるデータベースを使います。手を動かして確認したい方はこちらを見てデータベースを用意していただければと思います。
今回使用するカスタマーテーブルは以下のようになっています。
select * from customer order by customer_id asc ;
customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+-------------+--------------+------------------------------------------+------------+------------+-------------+----------------------------+-------- 1 | 1 | Mary | Smith | mary.smith@sakilacustomer.org | 5 | t | 2006-02-14 | 2016-07-28 12:14:31.748732 | 1 2 | 1 | Patricia | Johnson | patricia.johnson@sakilacustomer.org | 6 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 3 | 1 | Linda | Williams | linda.williams@sakilacustomer.org | 7 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 4 | 2 | Barbara | Jones | barbara.jones@sakilacustomer.org | 8 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 5 | 1 | Elizabeth | Brown | elizabeth.brown@sakilacustomer.org | 9 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 6 | 2 | Jennifer | Davis | jennifer.davis@sakilacustomer.org | 10 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 7 | 1 | Maria | Miller | maria.miller@sakilacustomer.org | 11 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 8 | 2 | Susan | Wilson | susan.wilson@sakilacustomer.org | 12 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 9 | 2 | Margaret | Moore | margaret.moore@sakilacustomer.org | 13 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 10 | 1 | Dorothy | Taylor | dorothy.taylor@sakilacustomer.org | 14 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 11 | 2 | Lisa | Anderson | lisa.anderson@sakilacustomer.org | 15 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 12 | 1 | Nancy | Thomas | nancy.thomas@sakilacustomer.org | 16 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 13 | 2 | Karen | Jackson | karen.jackson@sakilacustomer.org | 17 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 14 | 2 | Betty | White | betty.white@sakilacustomer.org | 18 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 15 | 1 | Helen | Harris | helen.harris@sakilacustomer.org | 19 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 16 | 2 | Sandra | Martin | sandra.martin@sakilacustomer.org | 20 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 0 17 | 1 | Donna | Thompson | donna.thompson@sakilacustomer.org | 21 | t | 2006-02-14 | 2016-07-28 12:14:29.088828 | 1 (599 row) 以下省略
ではWhere句を使ってもHaving句を使っても同じ結果を出ることを見てみます。
今回はstore_idが1の店舗ユーザーの合計を抽出するSQLを書いてみます。
Where句を使って抽出
select store_id ,count(customer_id) as customer_number from customer where store_id = 1 group by store_id ;
store_id | customer_number ----------+----------------- 1 | 326
Having句を使って抽出
select store_id ,count(customer_id) as customer_number from customer group by store_id having store_id =1 ;
store_id | customer_number ----------+----------------- 1 | 326
上の2つのSQLの違いはハイライトしている2行ずつです。
Where句を使ってもHaving句を使っても同様の結果を得ることができますね。
では内部的にも同じ動作をしているかというとそうではありません。
SQLが実行される順序は以下のようになっており
FROM → WHERE → GROUPBY → HAVING → SELECT → ORDERBY
GroupByでグルーピングする前に抽出するのがWhere句で
GroupByでグルーピングした後に抽出するのがHaving句になります。
もう少し詳しく見てみます。
Where句を使う場合
まずテーブルWhere句で指定された抽出条件(今回のケースではstore_id = 1)を実行します。
select store_id from customer where store_id = 1 ;
store_id ---------- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 (326 row) 以下省略 ※GroupByを実行していないので、Select句のCount(*)は実行できないため省略
store_id = 1 のレコードを抽出しています。その結果に対してGroupBy句を実行します。
select store_id ,count(customer_id) as customer_number from customer where store_id = 1 group by store_id;
store_id | customer_number ----------+----------------- 1 | 326
Having句の場合
From句の後にGroupBy句が実行されます。
select store_id ,count(customer_id) as customer_number from customer group by store_id ;
store_id | customer_number ----------+----------------- 1 | 326 2 | 273
GroupBy句でstore_idごとにグルーピングされています。
その結果に対してHaving句で指定した条件(今回の場合はstore_id = 1)で抽出します。
select store_id ,count(customer_id) as customer_number from customer group by store_id having store_id =1 ;
store_id | customer_number ----------+----------------- 1 | 326
同じ結果は得られますが、動作順序が違うことがわかりますね。
最後に
SQLの実行される順番がわかれば、どの句を使えばいいのかがわかるようになります。
こちらの記事と合わせて書き順も覚えていただければ幸いです。